package spitfire.ksim.algorithm;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class SimilarityAlgorithm {
	public static final double BETA = 0.01;
	
	public static double calculateScore(List<Double> dataList, FuzzyRule rule) {
		if (rule.size() < 2) {
			throw new RuntimeException("size too small");
		}
		double rawMax = Collections.max(dataList);
		double rawMin = Collections.min(dataList);
		
		ArrayList<Double> xList = rule.getxList();
		ArrayList<Double> yList = rule.getyList();
		double ruleRMax = rule.getrMax();
		double ruleRMin = rule.getrMin();
		
		double drange = Math.abs(rawMin-ruleRMin) + Math.abs(rawMax-ruleRMax);
		double rawRange = rawMax - rawMin;
		double ruleRange = ruleRMax - ruleRMin;
		
		double ndgSum = 0;
		for (int i = 0; i < dataList.size(); i++) {
			double dataValue = dataList.get(i);
			int p1 = Collections.binarySearch(xList, dataValue);
			int p2 = 0;
			if (p1 >= 0 ) {
				// found
				ndgSum += yList.get(p1);
			} else {
				// not found, convert to insertion point, @see Collections.binarySearch
				p1 = -p1 - 1;
				if (p1 == 0) {
					// smaller than min
				} else if (p1 == rule.size()) {
					// bigger than max
//					p1 = p1 - 2;
				} else {
					// data value between rule range
					p1--;
					p2 = p1 + 1;
					double x1 = xList.get(p1);
					double x2 = xList.get(p2);
					double y1 = yList.get(p1);
					double y2 = yList.get(p2);
					ndgSum += (x1*y2-y1*x2)/(x1-x2) + (y2-y1)/(x2-x1)*dataValue;
				}
			}
		}
		double ndgAvg = ndgSum / dataList.size();
		double sc = ndgAvg / drange + BETA * f(rawRange, ruleRange);
		System.out.println("ndgAvg=" + ndgAvg + "; drange=" + drange + "; rawRange=" + rawRange + "; ruleRange=" + ruleRange);
		System.out.println("ndgAvg/drange=" + (ndgAvg / drange) + "; f()=" + f(rawRange, ruleRange));
		return sc;
	}

	private static double f(double rawRange, double ruleRange) {
		double ratio = (ruleRange < rawRange) ? (ruleRange / rawRange) : (rawRange / ruleRange);
		return ratio * ratio;
	}
}
